// ----------------------------------------------------------------------------
//
// A class that implements an alarm clock.
//
// ----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Timers;
using Microsoft.VisualBasic.Devices;

namespace WERServices
{
    /// <summary>
    /// A class that implements an alarm clock.
    /// </summary>
    public class AlarmClock : IDisposable
    {
        #region Fields
        private Clock clock;
        private Alarm alarm;
        private bool isAlarmSet = false;
        public event WakeUpEventHandler WakeUp;
        private DateTime artificialClockTime;
        private Timer timer;

        #endregion Fields

        #region Constructors
        /// <summary>
        /// Initialize the time.
        /// </summary>
        public AlarmClock()
        {
            clock = new Clock();
        }

        /// <summary>
        /// Constructor to initialize an artificial clock time,
        /// used for creating an AlarmClock mock object for unit testing.
        /// </summary>
        /// <param name="clockTime"></param>
        public AlarmClock(DateTime clockTime)
        {
            this.artificialClockTime = clockTime;
        }
        #endregion Constructors

        #region Properties
        /// <summary>
        /// Gets the current date time.
        /// </summary>
        public DateTime CurrentDateTime
        {
            get
            {
                if (this.clock != null)
                {
                    return clock.LocalTime;
                }
                else
                {
                    return this.artificialClockTime;
                }
            }
        }

        /// <summary>
        /// Gets the alarm that is set. Returns null if the alarm is not set.
        /// Use the IsAlarmSet property to check if the alarm has been set.
        /// </summary>
        public Alarm Alarm
        {
            get { return this.alarm; }
        }

        /// <summary>
        /// Gets a boolean value indicating if the alarm has been set.
        /// </summary>
        public bool IsAlarmSet
        {
            get { return this.isAlarmSet; }
        }
        #endregion Properties

        #region Methods
        /// <summary>
        /// Method to set the alarm.
        /// </summary>
        /// <param name="alarm">Alarm object specifying the alarm.</param>
        public void SetAlarm(int hours, int minutes, Alarm.TimeNotation timeNotation, Alarm.Repeat repeat)
        {
            this.alarm = new Alarm(hours, minutes, timeNotation, repeat);
            this.isAlarmSet = true;

            //
            // before we start the timer check the time, because the timer will be fired only after one minute,
            // we need to check the time immediately first.
            //
            this.CheckTheTime();

            //
            // start the minute timer. Every minute check whether the current time is equal to the alarm time.
            // If it is then raise the WakeUpEvent event.
            //
            timer = new Timer();
            timer.Interval = (60 * 1000); // interval is in milliseconds, interval of 1 minute.
            timer.AutoReset = true;
            timer.Elapsed += new ElapsedEventHandler(TimerElapsed);
            timer.Start();
        }

        /// <summary>
        /// Method to raise the wake up event.
        /// </summary>
        /// <param name="e">Arguments for the WakeUp event.</param>
        protected virtual void OnWakeUpEvent(WakeUpEventArgs e)
        {
            //
            // stop the timer and dispose.
            //
            this.DisposeTimer();

            //
            // raise the event.
            //
            WakeUp(this, e);
        }

        /// <summary>
        /// Method to dispose the timer.
        /// </summary>
        private void DisposeTimer()
        {
            //
            // stop the timer and dispose.
            //
            if (this.timer != null)
            {
                this.timer.Stop();
                this.timer.Dispose();
                this.timer = null;
            }
        }

        /// <summary>
        /// Method as event handler for timer elapsed event. Calls the CheckTheTime method.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">An ElapsedEventArgs object that contains the event data.</param>
        public void TimerElapsed(object sender, ElapsedEventArgs e)
        {
            this.CheckTheTime();
        }

        /// <summary>
        /// Method to compare the current time with the alarm time.
        /// </summary>
        private void CheckTheTime()
        {
            //
            // get the current hour and minutes.
            //
            int hours = this.CurrentDateTime.Hour;
            int minutes = this.CurrentDateTime.Minute;
            Alarm.TimeNotation timeNotation = Alarm.TimeNotation.AM;

            //
            // the Hour property denotes the hours in 0 through 23
            // deduce the hours in 12-hour format
            //
            if (hours > 12)
            {
                hours = hours - 12;
                timeNotation = Alarm.TimeNotation.PM;
            }

            //
            // compare the time with the alarm
            //
            if (hours == this.Alarm.Hours && minutes == this.Alarm.Minutes && timeNotation == this.Alarm.AMPM)
            {
                //
                // it's time to wake up. Raise the event
                //
                WakeUpEventArgs wakeupEventArgs = new WakeUpEventArgs(this.Alarm);
                OnWakeUpEvent(wakeupEventArgs);
            }
        }
        #endregion Methods

        #region IDisposable Members
        public void Dispose()
        {
            //
            // stop the timer and dispose.
            //
            this.DisposeTimer();
        }
        #endregion IDisposable Members
    }
}
